package gqz.avdemo.cam;

import android.app.Activity;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.YuvImage;
import android.hardware.camera2.CameraCharacteristics;
import android.os.Environment;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.view.WindowManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Description
 *
 * @author gqz
 * @date 2020/12/24
 */
public class CamUtils {

   private static final String TAG = "CamUtils-->";

   /**
    * YUV 数据旋转90度 由于u、v均为y的1/4，所以4个y对应1个uv
    * <p>
    * I420: Y1  Y2  Y3  Y4 Y5  Y6  Y7  Y8 Y9  Y10 Y11 Y12 Y13 Y14 Y15 Y16 U1  U2  U3  U4 V1  V2  V3
    * V4
    * <p>
    * 顺时针旋转90度：
    * <p>
    * Y13 Y9  Y5 Y1 Y14 Y10 Y6 Y2 Y15 Y11 Y7 Y3 Y16 Y12 Y8 Y4 U3  U1  U4 U2 V3  V1  V4 V2
    */
   public static byte[] rotateYuvData(byte[] y, byte[] u, byte[] v, int w, int h) {
      byte[] yuv = new byte[y.length + u.length + v.length];

      //rotate y
      int yindex = 0;
      for (int i = 0; i < w; i++) {
         for (int j = h - 1; j >= 0; j--) {
            yuv[yindex++] = y[j * w + i];
         }
      }

      //rotate uv
      int uvindex = 0;
      for (int i = 0; i < w / 2; i++) {
         for (int j = h / 2 - 1; j >= 0; j--) {
            yuv[y.length + uvindex] = u[w / 2 * j + i];
            yuv[y.length + u.length + uvindex++] = v[w / 2 * j + i];
         }
      }

      return yuv;
   }

   /**
    * 画面方向矫正
    */
   public static int getDisplayRotation(CameraCharacteristics cameraCharacteristics,
                                        Activity act) {
      int rotation = act.getWindowManager().getDefaultDisplay().getRotation();//手机屏幕显示方向
      int degrees;
      switch (rotation) {
         case Surface.ROTATION_0:
            degrees = 0;
            break;
         case Surface.ROTATION_90:
            degrees = 90;
            break;
         case Surface.ROTATION_180:
            degrees = 180;
            break;
         case Surface.ROTATION_270:
            degrees = 270;
            break;
         default:
            degrees = 0;
      }
      int sensorOrientation = cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
      if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {
         return (360 - (sensorOrientation + degrees) % 360) % 360;
      } else {
         return (sensorOrientation - degrees + 360) % 360;
      }
   }

   public static byte[] I420Tonv21(byte[] data, int width, int height) {
      byte[] ret = new byte[data.length];
      int total = width * height;

      ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total);
      ByteBuffer bufferVU = ByteBuffer.wrap(ret, total, total / 2);

      bufferY.put(data, 0, total);
      for (int i = 0; i < total / 4; i += 1) {
         bufferVU.put(data[i + total + total / 4]);
         bufferVU.put(data[total + i]);
      }

      return ret;
   }

   public static void saveJPEG(YuvImage yuvImage) {
      File mImageFile = new File(Environment.getExternalStorageDirectory() + "/DCIM/myPicture.jpg");
      File mImageFile2 = new File(Environment.getExternalStorageDirectory() + "/DCIM/myPicture2.jpg");
      FileOutputStream fos = null;
      try {
         fos = new FileOutputStream(mImageFile);
         Rect rect = new Rect(0, 0, yuvImage.getWidth(), yuvImage.getHeight());
         yuvImage.compressToJpeg(rect, 100, fos);//1.6M
         fos.flush();
         fos.close();
         fos = new FileOutputStream(mImageFile2);
         yuvImage.compressToJpeg(rect, 80, fos);//239.36KB
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
         mImageFile = null;
         if (fos != null) {
            try {
               fos.close();
               fos = null;
            } catch (IOException e) {
               e.printStackTrace();
            }
         }
      }
   }

   public static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
      // Collect the supported resolutions that are at least as big as the preview Surface
      List<Size> bigEnough = new ArrayList<>();
      int w = aspectRatio.getWidth();
      int h = aspectRatio.getHeight();
      for (Size option : choices) {
         if (option.getHeight() == option.getWidth() * h / w &&
                 option.getWidth() >= width && option.getHeight() >= height) {
            bigEnough.add(option);
         }
      }

      // Pick the smallest of those, assuming we found any
      if (bigEnough.size() > 0) {
         return Collections.min(bigEnough, new CompareSizesByArea());
      } else {
         Log.e(TAG, "Couldn't find any suitable preview size");
         return choices[0];
      }
   }

   public static Size chooseOptimalSize2(Size[] choices, int textureViewWidth,
                                         int textureViewHeight, Size aspectRatio) {

      int maxWidth = 1920, maxHeight = 1080;
      // Collect the supported resolutions that are at least as big as the preview Surface
      List<Size> bigEnough = new ArrayList<>();
      // Collect the supported resolutions that are smaller than the preview Surface
      List<Size> notBigEnough = new ArrayList<>();
      int w = aspectRatio.getWidth();
      int h = aspectRatio.getHeight();
      for (Size option : choices) {
         if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
                 option.getHeight() == option.getWidth() * h / w) {
            if (option.getWidth() >= textureViewWidth &&
                    option.getHeight() >= textureViewHeight) {
               bigEnough.add(option);
            } else {
               notBigEnough.add(option);
            }
         }
      }

      // Pick the smallest of those big enough. If there is no one big enough, pick the
      // largest of those not big enough.
      if (bigEnough.size() > 0) {
         return Collections.min(bigEnough, new CompareSizesByArea());
      } else if (notBigEnough.size() > 0) {
         return Collections.max(notBigEnough, new CompareSizesByArea());
      } else {
         Log.e(TAG, "Couldn't find any suitable preview size");
         return choices[0];
      }
   }

   /**
    * Compares two {@code Size}s based on their areas.
    */
   static class CompareSizesByArea implements Comparator<Size> {

      @Override
      public int compare(Size lhs, Size rhs) {
         // We cast here to ensure the multiplications won't overflow
         return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
                 (long) rhs.getWidth() * rhs.getHeight());
      }

   }

   static public void configureTransform(Activity activity, TextureView mTextureView,
                                         int viewWidth, int viewHeight, Size mPreviewSize) {
      if (null == mTextureView || null == mPreviewSize || null == activity) {
         return;
      }
      int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
      Matrix matrix = new Matrix();
      RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
      RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
      float centerX = viewRect.centerX();
      float centerY = viewRect.centerY();
      if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
         bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
         matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
         float scale = Math.max(
                 (float) viewHeight / mPreviewSize.getHeight(),
                 (float) viewWidth / mPreviewSize.getWidth());
         matrix.postScale(scale, scale, centerX, centerY);
         matrix.postRotate(90 * (rotation - 2), centerX, centerY);
      }
      mTextureView.setTransform(matrix);
   }

}
